package com.ruoyi.common.utils.file;

import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPConnectionClosedException;
import org.apache.commons.net.ftp.FTPReply;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

@Slf4j
public class FtpUtils {

    /**
     * 上传文件夹全部文件
     *
     * @param ftpClient 连接ftp
     * @param folderDir 文件夹路径
     * @param targetDir 上传到的ftp路径
     * @return 是否成功
     */
    public static void uploadFolder(FTPClient ftpClient, String folderDir, String targetDir) {
        try {
            boolean b = ftpClient.changeWorkingDirectory(targetDir);
            if (!b) {
                // 如果目录不存在，则创建目录
                b = makeDir(ftpClient, targetDir);
            }
            if (!b) {
                log.error("FTP创建切换目录失败 " + targetDir);
                return;
            }
            log.info("FTP上传文件 folderDir: {}, targetDir: {}", folderDir, targetDir);
            File folderFile = new File(folderDir);
            File[] files = folderFile.listFiles();
            if (files != null) {
                for (File file : files) {
                    boolean done = false;
                    String fileName = file.getName();
                    if (file.length() == 0) {
                        log.error("FTP上传文件为空：" + folderDir + fileName);
                        continue;
                    }
                    try (final FileInputStream input = new FileInputStream(file)) {
                        // 上传文件
                        ftpClient.changeWorkingDirectory(targetDir);
                        boolean fileExistYn = fileExist(ftpClient, fileName);
                        if (fileExistYn) {
                            log.info("FTP上传文件已存在: " + targetDir + fileName);
                        } else {
                            done = ftpClient.storeFile(fileName, input);
                        }
                    }
                    if (done) {
                        log.info("FTP上传文件成功: " + targetDir + fileName);
                    } else {
                        log.error("FTP上传文件失败: " + targetDir + fileName);
                        showServerReply(ftpClient);
                    }
                }
                ftpClient.noop();
                ftpClient.logout();
            }
        } catch (FTPConnectionClosedException e) {
            log.error("FTP服务端关闭连接");
            e.printStackTrace();
        } catch (IOException e) {
            log.error("FTP客户端登录或操作失败");
            e.printStackTrace();
        } finally {
            // 断开连接
            disconnect(ftpClient);
        }
    }

    public static boolean uploadFile(FTPClient ftpClient, File file, String remote) {
        boolean done = false;
        String fileName = FileUtils.getName(remote);
        String targetDir = StringUtils.replace(remote, fileName, "");
        log.info("FTP上传文件 targetDir: {} fileName: {}", targetDir, fileName);
        try {
            try (final FileInputStream input = new FileInputStream(file)) {
                // 切换到目标目录
                boolean b = ftpClient.changeWorkingDirectory(targetDir);
                if (!b) {
                    // 如果目录不存在，则创建目录
                    b = makeDir(ftpClient, targetDir);
                }
                // 上传文件
                if (b) {
                    done = ftpClient.storeFile(fileName, input);
                } else {
                    log.error("FTP创建切换目录失败 " + targetDir);
                }
            }
            if (done) {
                log.info("FTP上传文件成功 " + fileName);
//                ftpClient.completePendingCommand();
            } else {
                log.error("FTP上传文件失败 " + fileName);
                showServerReply(ftpClient);
            }
            ftpClient.noop();
            ftpClient.logout();
        } catch (FTPConnectionClosedException e) {
            log.error("FTP服务端关闭连接");
            e.printStackTrace();
        } catch (IOException e) {
            log.error("FTP客户端登录或操作失败");
            e.printStackTrace();
        } finally {
//            disconnectQuietly(ftpClient);
            // 断开连接
            disconnect(ftpClient);
        }
        return done;
    }

    /**
     * ftp创建目录——ftpClient只支持一级一级创建
     */
    public static boolean makeDir(FTPClient ftpClient, String targetDir) throws IOException {
        // 分割
        String[] paths = targetDir.split("\\/");
        // 遍历每一级路径
        String path = "";
        for (String str : paths) {
            if (StringUtils.isBlank(path)) {
                path = str;
            } else {
                path = path + "/" + str;
            }
            // 该级路径不存在就创建并切换
            if (!ftpClient.changeWorkingDirectory(path)) {
                ftpClient.makeDirectory(path);
            } else {
                ftpClient.changeWorkingDirectory("/");
            }
        }
        return ftpClient.changeWorkingDirectory(targetDir);
    }

    public static boolean checkFileExists(FTPClient ftpClient, String filePath) throws IOException {
        InputStream inputStream = ftpClient.retrieveFileStream(filePath);
        int returnCode = ftpClient.getReplyCode();
        if (inputStream == null || returnCode == 550) {
            return false;
        }
        return true;
    }

    public static boolean fileExist(FTPClient ftpClient, String filePath) {
        try {
            return ftpClient.listFiles(filePath).length > 0;
        } catch (IOException e) {
            log.error("FTP服务器异常：" + e.getMessage());
        }
        return false;
    }

    public static FTPClient connection(String host, Integer port, String userName, String password) {
        // ftp
        FTPClient ftpClient = new FTPClient();
        // 设置连接使用的字符编码。必须在建立连接之前设置。
        ftpClient.setControlEncoding("UTF-8");
        try {
            // 连接服务端
            ftpClient.connect(host, port);
            log.info("FTP连接服务器" + host + ":" + port);
            // ftp操作可能会返回一些响应信息，可以打印出来看看
            showServerReply(ftpClient);

            // 尝试连接后，检查响应码以确认成功
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                disconnect(ftpClient);
                log.error("FTP服务器拒绝连接");
                return null;
            }
            // 登录ftp
            boolean success = ftpClient.login(userName, password);
            if (!success) {
                ftpClient.logout();
                log.error("FTP客户端登录失败 userName: {}, password: {}", userName, password);
                return null;
            }
            log.info("FTP客户端登录成功");
            // 大部分情况，上传文件时，需要设置这两项
            // 设置文件传输类型为二进制文件类型
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            // 设置被动模式
            ftpClient.enterLocalPassiveMode();
        } catch (IOException e) {
            disconnect(ftpClient);
            log.error("FTP连接失败");
            e.printStackTrace();
            return null;
        }
        return ftpClient;
    }

    /**
     * 断开ftp连接
     */
    public static void disconnect(FTPClient ftpClient) {
        if (ftpClient != null && ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
                log.info("FTP断开连接");
            } catch (IOException ex) {
                // do nothing
            }
        }
    }

    /**
     * 打印服务器返回信息
     */
    public static void showServerReply(FTPClient ftpClient) {
        String[] replies = ftpClient.getReplyStrings();
        if (replies != null && replies.length > 0) {
            for (String aReply : replies) {
                log.info("FTP服务端响应信息: " + aReply);
            }
        }
    }
}

